use std::collections::HashMap;
use std::iter::zip;

#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum HandType {
    FiveOfAKind = 0,
    FourOfAKind,
    FullHouse,
    ThreeOfAKind,
    TwoPair,
    OnePair,
    HighCard,
}

#[derive(PartialEq, Debug)]
pub struct CamelCard {
    hand_literal: String,
    hand_type: HandType,
    bid: u32, 
}

impl CamelCard {
    pub fn bid(&self) -> u32 {
        self.bid
    } 
}

pub trait CamelCardMaker {
    fn get_hand_type(&self, s: &str) -> HandType;

    fn get_char_strength(&self, ch: char) -> Option<u32>;

    fn compare(&self, card1: &CamelCard, card2: &CamelCard) -> std::cmp::Ordering {
        match card1.hand_type.cmp(&card2.hand_type) {
            std::cmp::Ordering::Equal => {
                for (card1_ch, card2_ch) in zip(card1.hand_literal.chars(), card2.hand_literal.chars()) {
                    let card1_strength = self.get_char_strength(card1_ch).unwrap();
                    let card2_strength = self.get_char_strength(card2_ch).unwrap();
                    match card1_strength.cmp(&card2_strength) {
                        std::cmp::Ordering::Equal   => continue,
                        std::cmp::Ordering::Greater => return std::cmp::Ordering::Less,
                        std::cmp::Ordering::Less    => return std::cmp::Ordering::Greater,
                    }
                }
                return std::cmp::Ordering::Equal;
            },
            std::cmp::Ordering::Greater => return std::cmp::Ordering::Less,
            std::cmp::Ordering::Less    => return std::cmp::Ordering::Greater,
        }
    }
    
    fn get_camel_card(&self, s: &str) -> CamelCard {
        let mut tokens = s.split(" ");
        let hand_str = tokens.next().unwrap();
        let bid_str = tokens.next().unwrap();
    
        let hand_literal = hand_str.to_string();
        let bid: u32 = bid_str.parse().unwrap();
    
        let hand_type = self.get_hand_type(hand_str);
    
        CamelCard { hand_literal, hand_type, bid }
    }
}

pub struct Part1CamelCardMaker {
    map: HashMap<char, u32>,
}

impl Part1CamelCardMaker {
    pub fn new() -> Part1CamelCardMaker {
        Part1CamelCardMaker {
            map: HashMap::<char, u32>::from([
                ('A', 0),
                ('K', 1),
                ('Q', 2),
                ('J', 3),
                ('T', 4),
                ('9', 5),
                ('8', 6),
                ('7', 7),
                ('6', 8),
                ('5', 9),
                ('4', 10),
                ('3', 11),
                ('2', 12),
            ])
        }
    } 
}

impl CamelCardMaker for Part1CamelCardMaker {
    fn get_hand_type(&self, s: &str) -> HandType {
        let mut char_counter: HashMap<char, u32> = HashMap::new();
        s.chars().for_each(|ch| {
            if let Some(count) = char_counter.get_mut(&ch) {
                *count += 1;
            } else {
                char_counter.insert(ch, 1);
            }
        });
    
        let &max_count = char_counter.values().max().unwrap();
        let &min_count = char_counter.values().min().unwrap();
        let char_kind_size = char_counter.len();
        match max_count {
            5 => return HandType::FiveOfAKind,
            4 => return HandType::FourOfAKind,
            3 => {
                return if min_count == 1 { HandType::ThreeOfAKind } else { HandType::FullHouse }
            },
            2 => {
                return if char_kind_size == 3 { HandType::TwoPair } else { HandType::OnePair }
            },
            1 => return HandType::HighCard,
            _ => (),
        };
        panic!("nonono");
    }

    fn get_char_strength(&self, ch: char) -> Option<u32> {
        if let Some(v) = self.map.get(&ch) {
            return Some(v.clone());
        }
        None
    }
}



pub struct Part2CamelCardMaker {
    map: HashMap<char, u32>,
}

impl Part2CamelCardMaker {
    pub fn new() -> Part2CamelCardMaker {
        Part2CamelCardMaker {
            map: HashMap::<char, u32>::from([
                ('A', 0),
                ('K', 1),
                ('Q', 2),
                ('T', 4),
                ('9', 5),
                ('8', 6),
                ('7', 7),
                ('6', 8),
                ('5', 9),
                ('4', 10),
                ('3', 11),
                ('2', 12),
                ('J', 13),
            ])
        }
    } 
}

impl CamelCardMaker for Part2CamelCardMaker {
    fn get_hand_type(&self, s: &str) -> HandType {
        let mut char_counter: HashMap<char, u32> = HashMap::new();
        for ch in s.chars() {
            if let Some(count) = char_counter.get_mut(&ch) {
                *count += 1;
            } else {
                char_counter.insert(ch, 1);
            }
        }
    
        let &max_count = char_counter.values().max().unwrap();
        let &min_count = char_counter.values().min().unwrap();
        let char_kind_size = char_counter.len();
        let j_size = if let Some(&count) = char_counter.get(&'J') {
            count
        } else {
            0
        };
        
        // match char_kind_size {
        //     1 => {
        //         return HandType::FiveOfAKind;
        //     }
        //     2 => {
        //         if min_count == 1 {
        //             if j_size != 0 {
        //                 return HandType::FiveOfAKind;
        //             } else {
        //                 return HandType::FourOfAKind;
        //             }
        //         } else {
        //             if j_size != 0 {
        //                 return HandType::FiveOfAKind;
        //             } else {
        //                 return HandType::FullHouse;
        //             }
        //         }
        //     }
        //     3 => {
        //         if max_count == 3 {
        //             if j_size == 0 {
        //                 return HandType::ThreeOfAKind;
        //             } else {
        //                 return HandType::FourOfAKind;
        //             }
        //         } else {
        //             if j_size == 0 {
        //                 return HandType::TwoPair;
        //             } else if j_size == 2 {
        //                 return HandType::FourOfAKind;
        //             } else {
        //                 return HandType::FullHouse;
        //             }
        //         }
        //     }
        //     4 => {
        //         if j_size != 0 {
        //             return HandType::ThreeOfAKind;
        //         } else {
        //             return HandType::OnePair;
        //         }
        //     }
        //     5 => {
        //         if j_size != 0 {
        //             return HandType::OnePair;
        //         } else {
        //             return HandType::HighCard;
        //         }
        //     }
        //     _ => (),
        // }

        match max_count {
            5 => return HandType::FiveOfAKind,
            4 => {
                // AAAAB
                if j_size != 0 {
                    // AAAAA
                    return HandType::FiveOfAKind;
                } else {
                    // AAAAB
                    return HandType::FourOfAKind;
                }
            },
            3 => {
                if min_count == 1 {
                    // AAABC
                    if j_size != 0 {
                        // AAAAC | BBBBC
                        return HandType::FourOfAKind;
                    } else {
                        // AAABC
                        return HandType::ThreeOfAKind;
                    }
                } else {
                    // AAABB
                    if j_size != 0 {
                        // AAAAA
                        return HandType::FiveOfAKind;
                    } else {
                        // AAABB
                        return HandType::FullHouse;
                    }
                }
            },
            2 => {
                if char_kind_size == 3 {
                    // AABBC
                    if j_size == 0 {
                        // AABBC
                        return HandType::TwoPair;
                    } else if j_size == 1 {
                        // AAABB
                        return HandType::FullHouse;
                    } else {
                        // AAAAC
                        return HandType::FourOfAKind;
                    }
                } else {
                    // AABCD
                    if j_size == 0 {
                        // AABCD
                        return HandType::OnePair;
                    } else if j_size == 1 {
                        // AAACD
                        return HandType::ThreeOfAKind;
                    } else {
                        // BBBCD
                        return HandType::ThreeOfAKind;
                    }
                }
            },
            1 => {
                // ABCDE
                if j_size == 0 {
                    // ABCDE
                    return HandType::HighCard;
                } else {
                    // AACDE
                    return HandType::OnePair;
                }
            },
            _ => (),
        };
        panic!("nonono");
    }

    fn get_char_strength(&self, ch: char) -> Option<u32> {
        if let Some(v) = self.map.get(&ch) {
            return Some(v.clone());
        }
        None
    }
}


#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_create_camel_card_part1() {
        let maker = Part1CamelCardMaker::new();
        let cc = maker.get_camel_card("JJJJJ 435");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "JJJJJ".to_string(), 
                hand_type: HandType::FiveOfAKind,
                bid: 435
            }
        );

        let cc = maker.get_camel_card("AA8AA 9");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "AA8AA".to_string(), 
                hand_type: HandType::FourOfAKind,
                bid: 9
            }
        );

        let cc = maker.get_camel_card("23332 76");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "23332".to_string(), 
                hand_type: HandType::FullHouse,
                bid: 76
            }
        );

        let cc = maker.get_camel_card("TTT98 19");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "TTT98".to_string(), 
                hand_type: HandType::ThreeOfAKind,
                bid: 19
            }
        );

        let cc = maker.get_camel_card("23432 12");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "23432".to_string(), 
                hand_type: HandType::TwoPair,
                bid: 12
            }
        );

        let cc = maker.get_camel_card("A23A4 887");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "A23A4".to_string(), 
                hand_type: HandType::OnePair,
                bid: 887
            }
        );

        let cc = maker.get_camel_card("23456 122");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "23456".to_string(), 
                hand_type: HandType::HighCard,
                bid: 122
            }
        );
    }

    #[test]
    fn test_create_camel_card_part2() {
        let maker = Part2CamelCardMaker::new();

        let cc = maker.get_camel_card("32T3K 765");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "32T3K".to_string(), 
                hand_type: HandType::OnePair,
                bid: 765
            }
        );

        let cc = maker.get_camel_card("T55J5 684");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "T55J5".to_string(), 
                hand_type: HandType::FourOfAKind,
                bid: 684
            }
        );

        let cc = maker.get_camel_card("KK677 28");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "KK677".to_string(), 
                hand_type: HandType::TwoPair,
                bid: 28
            }
        );

        let cc = maker.get_camel_card("KTJJT 220");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "KTJJT".to_string(), 
                hand_type: HandType::FourOfAKind,
                bid: 220
            }
        );

        let cc = maker.get_camel_card("QQQJA 483");
        assert_eq!(
            cc, 
            CamelCard {
                hand_literal: "QQQJA".to_string(), 
                hand_type: HandType::FourOfAKind,
                bid: 483
            }
        );
    }
}